﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
using VisionApp.Interface;
using VisionApp.Entity;
using PylonC.NET;
using System.Runtime.InteropServices;

namespace VisionApp.Camera
{
    public class BaslerCamera : ObservableObject, ICamera
    {
        public HImageContainer ImCtnr { get; set; }
        public List<string> DeviceIDList { get; set; }
        public CameraInfo CurrentCameraInfo { get; set; }
        public bool Connected { get; set; }

        //private const int _camNum = 2;
        //private uint numDevices;    /* Number of available devices. */
        //private const int numGrabs = 10; /* Number of images to grab. */
        private PylonBuffer<Byte> imgBuf = null;  /* Buffer used for grabbing. */
        private bool isAvail;
        private Logger _log;
        private Dictionary<string, PYLON_DEVICE_HANDLE> _deviceDict = new Dictionary<string, PYLON_DEVICE_HANDLE>();
        public BaslerCamera(ILogger log)
        {
            _log = (Logger)log;
            ImCtnr = new HImageContainer();
        }

        public int Init()
        {
            try
            {
#if DEBUG
                /* This is a special debug setting needed only for GigE cameras.
                See 'Building Applications with pylon' in the Programmer's Guide. */
                Environment.SetEnvironmentVariable("PYLON_GIGE_HEARTBEAT", "300000" /*ms*/);
#endif
                /* Before using any pylon methods, the pylon runtime must be initialized. */
                Pylon.Initialize();

                /* Enumerate all camera devices. You must call
                PylonEnumerateDevices() before creating a device. */
                uint numDevices = Pylon.EnumerateDevices();
                if (0 != numDevices)
                {
                    _log.Error("No device found.");
                    return -1;
                }
                _log.Imformation(string.Format("Found {0} devices", numDevices));
                for (uint i = 0; i < numDevices; i++)
                {
                    /* Get a handle for the first device found.  */
                    PYLON_DEVICE_HANDLE hDev = Pylon.CreateDeviceByIndex(i);
                    /* Before using the device, it must be opened. Open it for configuring
                    parameters and for grabbing images. */
                    Pylon.DeviceOpen(hDev, Pylon.cPylonAccessModeControl | Pylon.cPylonAccessModeStream);
                    string name = string.Empty;
                    bool isReadable = Pylon.DeviceFeatureIsReadable(hDev, "DeviceID");
                    if (isReadable)
                    {
                        name = Pylon.DeviceFeatureToString(hDev, "DeviceID");
                        if (!DeviceIDList.Contains(name))
                        {
                            Pylon.DeviceClose(hDev);
                            Pylon.DestroyDevice(hDev);
                            continue;
                        }
                        _log.Imformation(string.Format("Using camera {0}", name));
                    }
                    /* Set the pixel format to Mono8, where gray values will be output as 8 bit values for each pixel. */
                    /* ... Check first to see if the device supports the Mono8 format. */
                    isAvail = Pylon.DeviceFeatureIsAvailable(hDev, "EnumEntry_PixelFormat_Mono8");
                    if (!isAvail)
                    {
                        /* Feature is not available. */
                        throw new Exception("Device doesn't support the Mono8 pixel format.");
                    }
                    /* ... Set the pixel format to Mono8. */
                    Pylon.DeviceFeatureFromString(hDev, "PixelFormat", "Mono8");
                    /* Disable acquisition start trigger if available. */
                    isAvail = Pylon.DeviceFeatureIsAvailable(hDev, "EnumEntry_TriggerSelector_AcquisitionStart");
                    if (isAvail)
                    {
                        Pylon.DeviceFeatureFromString(hDev, "TriggerSelector", "AcquisitionStart");
                        Pylon.DeviceFeatureFromString(hDev, "TriggerMode", "Off");
                    }
                    /* Disable frame burst start trigger if available */
                    isAvail = Pylon.DeviceFeatureIsAvailable(hDev, "EnumEntry_TriggerSelector_FrameBurstStart");
                    if (isAvail)
                    {
                        Pylon.DeviceFeatureFromString(hDev, "TriggerSelector", "FrameBurstStart");
                        Pylon.DeviceFeatureFromString(hDev, "TriggerMode", "Off");
                    }
                    /* Disable frame start trigger if available */
                    isAvail = Pylon.DeviceFeatureIsAvailable(hDev, "EnumEntry_TriggerSelector_FrameStart");
                    if (isAvail)
                    {
                        Pylon.DeviceFeatureFromString(hDev, "TriggerSelector", "FrameStart");
                        Pylon.DeviceFeatureFromString(hDev, "TriggerMode", "Off");
                    }
                    /* For GigE cameras, we recommend increasing the packet size for better
                       performance. If the network adapter supports jumbo frames, set the packet
                       size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
                       to 1500. */
                    /* ... Check first to see if the GigE camera packet size parameter is supported
                        and if it is writable. */
                    isAvail = Pylon.DeviceFeatureIsWritable(hDev, "GevSCPSPacketSize");
                    if (isAvail)
                    {
                        /* ... The device supports the packet size feature. Set a value. */
                        Pylon.DeviceSetIntegerFeature(hDev, "GevSCPSPacketSize", 1500);
                    }
                    isAvail = Pylon.DeviceFeatureIsWritable(hDev, "ExposureTimeAbs");
                    if (isAvail)
                    {
                        Pylon.DeviceSetFloatFeature(hDev, "ExposureTimeAbs", CurrentCameraInfo.ExposureTime);
                    }
                    _deviceDict.Add(name, hDev);
                }
                return 0;
            }
            catch (Exception e)
            {
                _log.Error("Init Error " + e.ToString());
                return -1;
            }
        }

        public int GrabImg()
        {
            PylonGrabResult_t grabResult;
            try
            {
                /* Grab one single frame from stream channel 0. The
                camera is set to "single frame" acquisition mode.
                Wait up to 500 ms for the image to be grabbed.
                If imgBuf is null a buffer is automatically created with the right size.*/
                PYLON_DEVICE_HANDLE hDev;
                bool isAvail;
                hDev = _deviceDict[CurrentCameraInfo.DeviceID];
                isAvail = Pylon.DeviceFeatureIsWritable(hDev, "ExposureTimeAbs");
                if (isAvail)
                {
                    Pylon.DeviceSetFloatFeature(hDev, "ExposureTimeAbs", CurrentCameraInfo.ExposureTime);
                }
                if (!Pylon.DeviceGrabSingleFrame(hDev, 0, ref imgBuf, out grabResult, 500))
                {
                    /* Timeout occurred. */
                    _log.Error(string.Format("Frame {0}: timeout.", CurrentCameraInfo.DeviceID));
                }
                /* Check to see if the image was grabbed successfully. */
                if (grabResult.Status == EPylonGrabStatus.Grabbed)
                {
                    /* Success. Perform image processing. */
                    ImCtnr.Dispose();
                    ImCtnr = new HImageContainer();
                    ImCtnr.TextWidth = grabResult.SizeX;
                    ImCtnr.TextHeight = grabResult.SizeY;
                    ImCtnr.ImGray = imgBuf.Array;
                }
                else if (grabResult.Status == EPylonGrabStatus.Failed)
                {
                    _log.Error(string.Format("Frame {0} wasn't grabbed successfully.  Error code = {1}", CurrentCameraInfo.DeviceID, grabResult.ErrorCode));
                }
                return 0;
            }
            catch (Exception e)
            {
                _log.Error("Unknow GrabImg Error " + e.ToString());
                return -1;
            }

        }

        public int Exit()
        {
            try
            {
                /* Release the buffer. */
                if (imgBuf != null)
                {
                    imgBuf.Dispose();
                }
                foreach (var dev in _deviceDict)
                {
                    /* Clean up. Close and release the pylon device. */
                    Pylon.DeviceClose(dev.Value);
                    Pylon.DestroyDevice(dev.Value);
                }
                _deviceDict.Clear();
                /* Free memory for grabbing. */
                imgBuf = null;
                /* Shut down the pylon runtime system. Don't call any pylon method after
                   calling Pylon.Terminate(). */
                Pylon.Terminate();
                return 0;
            }
            catch (Exception e)
            {
                _log.Error("Unknow Close Error ", e);
                return -1;
            }
        }

        public int StopRecord()
        {
            throw new NotImplementedException();
        }
    }
}
